package com.weixing.mall.provider.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.weixing.mall.base.constant.GlobalConstant;
import com.weixing.mall.base.exception.BusinessException;
import com.weixing.mall.base.utils.EnumUtil;
import com.weixing.mall.base.wrapper.Result;
import com.weixing.mall.base.enums.WhetherEnum;
import com.weixing.mall.provider.api.model.dto.ProfitCalcDto;
import com.weixing.mall.provider.api.model.enums.OrderTypeEnum;
import com.weixing.mall.provider.api.model.enums.SettleStateEnum;
import com.weixing.mall.provider.api.model.vo.ActOrderSettleVo;
import com.weixing.mall.provider.api.model.vo.ActRpcVo;
import com.weixing.mall.provider.api.model.vo.OrderAgentVo;
import com.weixing.mall.provider.api.model.vo.OrderItemVo;
import com.weixing.mall.provider.api.service.ActivityRpcService;
import com.weixing.mall.provider.api.service.OrderAgentServiceRpc;
import com.weixing.mall.provider.api.service.OrderServiceRpc;
import com.weixing.mall.provider.api.service.SysConfigServiceFeign;
import com.weixing.mall.provider.model.domain.*;
import com.weixing.mall.provider.model.enums.BillTransTypeEnum;
import com.weixing.mall.provider.model.enums.BillTypeEnum;
import com.weixing.mall.provider.service.*;
import com.weixing.mall.util.number.MoneyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ClassName AgentProfitServiceImpl
 * @Description
 * @Author Small
 * @Date 2020/2/19 16:28
 * @Version 1.0
 **/
@Slf4j
@Service
public class AgentProfitServiceImpl implements IAgentProfitService {
    @Autowired
    private IAgentRebateService agentRebateService;
    @Autowired
    private IAgentCommissionService agentCommissionService;
    @Autowired
    private IAgentBonusService agentBonusService;
    @Autowired
    private IAgentAccountBillService agentAccountBillService;
    @Autowired
    private IAgentAccountService agentAccountService;
    @Autowired
    private SysConfigServiceFeign sysConfigServiceFeign;
    @Autowired
    private ActivityRpcService activityRpcService;
    @Autowired
    private OrderServiceRpc orderServiceRpc;
    @Autowired
    private OrderAgentServiceRpc orderAgentServiceRpc;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void calcProfit(ProfitCalcDto profitCalc) {
        if (log.isDebugEnabled()) {
            log.debug("AgentProfitServiceImpl收到代理获利计算请求，订单编号{}", profitCalc.getOrderSn());
        }
        // 先查询该笔订单是否已结算，如果已结算就不再重新计算
        Result<OrderItemVo> orderItemVoResult = orderServiceRpc.selectById(profitCalc.getOrderId());
        if (!orderItemVoResult.isSuccess()) {
            throw new BusinessException("订单服务暂时不可用");
        }
        if (SettleStateEnum.SETTLED.getValue().equals(orderItemVoResult.getData().getSettleState())) {
            return;
        }
        Result<List<OrderAgentVo>> orderAgentVoResult = orderAgentServiceRpc.selectByOrderId(profitCalc.getOrderId());
        if (!orderAgentVoResult.isSuccess()) {
            throw new BusinessException("订单服务暂时不可用");
        }
        List<AgentRebate> rebateList = Lists.newArrayList();
        List<AgentCommission> commissionList = Lists.newArrayList();
        List<AgentBonus> bonusList = Lists.newArrayList();


        List<OrderAgentVo> agentVoList = orderAgentVoResult.getData();
        if (CollUtil.isNotEmpty(agentVoList)) {
            for (OrderAgentVo vo : agentVoList) {
                switch (vo.getProfitType()) {
                    case GlobalConstant.PROFIT_TYPE_REBATE:
                        rebateList.add(getAgentRebate(profitCalc, vo));
                        break;
                    case GlobalConstant.PROFIT_TYPE_COMM:
                        commissionList.add(getAgentCommission(profitCalc, vo));
                        break;
                    case GlobalConstant.PROFIT_TYPE_BONUS:
                        bonusList.add(getAgentBonus(profitCalc, vo));
                        break;
                    default:
                        break;
                }
            }
            // 代理账单
            List<AgentAccountBill> agentAccountBillList = getAgentAccountBills(rebateList, commissionList, bonusList);
            // 代理账户
            List<AgentAccount> agentAccounts = batchUpdateAgentAccount(agentAccountBillList);
            // 持久化
            if (CollUtil.isNotEmpty(rebateList)) {
                agentRebateService.saveBatch(rebateList);
            }
            if (CollUtil.isNotEmpty(commissionList)) {
                agentCommissionService.saveBatch(commissionList);
            }
            if (CollUtil.isNotEmpty(bonusList)) {
                agentBonusService.saveBatch(bonusList);
            }
            if (CollUtil.isNotEmpty(agentAccountBillList)) {
                agentAccountBillService.saveBatch(agentAccountBillList);
            }
            if (CollUtil.isNotEmpty(agentAccounts)) {
                agentAccountService.updateBatchById(agentAccounts);
            }
        }
    }

    /**
     * 方法描述: 活动结算
     * @Author Small
     * @param actId
     * @Date 2020/4/13 14:05
     * @return void
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void actCalcProfit(Long actId) {
        Result<ActRpcVo> actRpcVoResult = activityRpcService.selectById(actId);
        if (!actRpcVoResult.isSuccess()) {
            log.warn("活动结算，查询活动rpc请求失败");
            return;
        }
        ActRpcVo actRpcVo = actRpcVoResult.getData();
        if (null == actRpcVo) {
            throw new BusinessException("活动不存在");
        }
        Integer settleStatus = actRpcVo.getSettleStatus();
        if (SettleStateEnum.SETTLED.getValue().equals(settleStatus) || SettleStateEnum.RAISE_SETTLE.getValue().equals(settleStatus)) {
            log.warn("活动已结算，或正在结算，请勿重复发起结算");
            return;
        }
        Result<List<ActOrderSettleVo>> ordersResult = orderServiceRpc.selectAgentActOrdersByActId(actId);
        List<ActOrderSettleVo> orderS = ordersResult.getData();
        if (CollUtil.isNotEmpty(orderS)) {
            ProfitCalcDto profitCalcDto = null;
            for (ActOrderSettleVo orderSettleVo : orderS) {
                profitCalcDto = new ProfitCalcDto();
                profitCalcDto.setOrderSn(orderSettleVo.getOrderSn());
                profitCalcDto.setKefu(orderSettleVo.getKefuName());
                profitCalcDto.setKefuId(orderSettleVo.getKefuId());
                profitCalcDto.setOrderGmtTime(orderSettleVo.getCreatedTime());
                profitCalcDto.setOrderAuditTime(orderSettleVo.getAuditTime());
                profitCalcDto.setActId(orderSettleVo.getActId());
                profitCalcDto.setAgentId(orderSettleVo.getAgentId());
                profitCalcDto.setOrderId(orderSettleVo.getId());
                calcProfit(profitCalcDto);
            }
        }
        List<Long> orderIds = orderS.stream().map(t -> t.getId()).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(orderIds)) {
            Result result = orderServiceRpc.actOrderSettleNotify(orderIds);
            if (!result.isSuccess()) {
                throw new BusinessException("结算失败");
            }
        }

    }

    /**
     * 退货收益回滚
     *
     * @param orderId
     */
    @Override
    public void unProfit(Long orderId) {
        Result<OrderItemVo> orderItemVoResult = orderServiceRpc.selectById(orderId);
        if (!orderItemVoResult.isSuccess()) {
            throw new BusinessException("订单服务不可用");
        }
        OrderItemVo data = orderItemVoResult.getData();
        if (null == data) {
            throw new BusinessException("订单不存在");
        }
        Result<List<OrderAgentVo>> orderAgentVoResult = orderAgentServiceRpc.selectByOrderId(orderId);
        if (!orderAgentVoResult.isSuccess()) {
            throw new BusinessException("订单服务暂时不可用");
        }
        List<OrderAgentVo> agentVoList = orderAgentVoResult.getData();
        if (CollUtil.isNotEmpty(agentVoList)) {
            List<AgentAccountBill> billList = Lists.newArrayList();
            for (OrderAgentVo vo : agentVoList) {
                switch (vo.getProfitType()) {
                    case GlobalConstant.PROFIT_TYPE_REBATE:
                        AgentRebate agentRebate = agentRebateService.findByOrderIdAndAgentId(orderId, vo.getAgentId());
                        if (null != agentRebate){
                            agentRebate.setIsEnable(WhetherEnum.NO.getValue());
                            agentRebate.setRemark("订单退货，扣除返利");
                            agentRebateService.updateById(agentRebate);
                            billList.add(createBillFromRebate(agentRebate, BillTypeEnum.OUTCOME, "退货，扣除返利"));
                        }
                        break;
                    case GlobalConstant.PROFIT_TYPE_COMM:
                        AgentCommission agentCommission = agentCommissionService.findByOrderIdAndAgentId(orderId, vo.getAgentId());
                        if (null != agentCommission){
                            agentCommission.setIsEnable(WhetherEnum.NO.getValue());
                            agentCommission.setRemark("订单退货，扣除佣金");
                            agentCommissionService.updateById(agentCommission);
                            billList.add(createBillFromCommission(agentCommission, BillTypeEnum.OUTCOME, "退货，扣除佣金"));
                        }
                        break;
                    case GlobalConstant.PROFIT_TYPE_BONUS:
                        AgentBonus agentBonus = agentBonusService.findByOrderIdAndAgentId(orderId, vo.getAgentId());
                        if (null != agentBonus){
                            agentBonus.setIsEnable(WhetherEnum.NO.getValue());
                            agentBonus.setRemark("订单退货，扣除分红");
                            agentBonusService.updateById(agentBonus);
                            billList.add(createBillFromBonus(agentBonus, BillTypeEnum.OUTCOME, "退货，扣除佣金"));
                        }
                        break;
                    default:
                        break;
                }
            }
            // 记录账户变动
            if (CollUtil.isNotEmpty(billList)) {
                agentAccountBillService.saveBatch(billList);
            }
            // 代理账户
            List<AgentAccount> agentAccounts = batchUpdateAgentAccount(billList);
            if (CollUtil.isNotEmpty(agentAccounts)) {
                agentAccountService.updateBatchById(agentAccounts);
            }
        }
    }


    private List<AgentAccountBill> getAgentAccountBills(List<AgentRebate> rebateList, List<AgentCommission> commissionList, List<AgentBonus> bonusList) {
        List<AgentAccountBill> accountBillList = Lists.newArrayList();
        accountBillList.addAll(getAgentAccountBillListFromRebateList(rebateList));
        accountBillList.addAll(getAgentAccountBillListFromBonusList(bonusList));
        accountBillList.addAll(getAgentAccountBillListFromCommList(commissionList));
        return accountBillList;
    }

    private AgentBonus getAgentBonus(ProfitCalcDto profitCalc, OrderAgentVo vo) {
        AgentBonus agentBonus = new AgentBonus();
        agentBonus.setAgentId(vo.getAgentId());
        agentBonus.setAgentSn(vo.getAgentSn());
        agentBonus.setOrderPrice(vo.getOrderPrice());
        agentBonus.setOrderSn(vo.getOrderSn());
        agentBonus.setBonusAmount(vo.getAgentProfit());
        agentBonus.setOrderId(vo.getOrderId());
        agentBonus.setOrderType(vo.getOrderType());
        agentBonus.setBonusRatio(vo.getBonusRatio());
        agentBonus.setAgentName(vo.getAgentName());
        agentBonus.setMobile(vo.getAgentMobile());

        agentBonus.setOrderGmtTime(profitCalc.getOrderGmtTime());
        agentBonus.setBonusTeamId(vo.getBonusTeamId());
        agentBonus.setBonusTeamName(vo.getBonusTeamName());
        agentBonus.setKefu(vo.getKefuName());
        agentBonus.setKefuId(vo.getKefuId());
        agentBonus.setBonusType(vo.getBonusType());
        return agentBonus;
    }

    private AgentCommission getAgentCommission(ProfitCalcDto profitCalc, OrderAgentVo vo) {
        AgentCommission agentCommission = new AgentCommission();
        agentCommission.setAgentId(vo.getAgentId());
        agentCommission.setOrderId(vo.getOrderId());
        agentCommission.setCommissionAmount(vo.getAgentProfit());
        agentCommission.setCommissionFrom(vo.getOrderType());
        agentCommission.setCommissionType(vo.getBelongType());
        agentCommission.setOrderSn(vo.getOrderSn());
        agentCommission.setOrderPrice(vo.getOrderPrice());
        agentCommission.setRemark("");
        agentCommission.setOrderGmtTime(profitCalc.getOrderGmtTime());
        agentCommission.setOrderAuditTime(profitCalc.getOrderAuditTime());
        agentCommission.setKefuId(profitCalc.getKefuId());
        agentCommission.setKefu(profitCalc.getKefu());
        agentCommission.setOrderAgentId(profitCalc.getAgentId());
        return agentCommission;
    }

    private AgentRebate getAgentRebate(ProfitCalcDto profitCalc, OrderAgentVo vo) {
        AgentRebate agentRebate = new AgentRebate();
        agentRebate.setAgentId(vo.getAgentId());
        agentRebate.setOrderId(vo.getOrderId());
        agentRebate.setRebateAmount(vo.getAgentProfit());
        agentRebate.setOrderPrice(vo.getOrderPrice());
        agentRebate.setOrderSn(vo.getOrderSn());
        // 和订单类型保持一致
        agentRebate.setRebateFrom(vo.getOrderType());
        agentRebate.setRebateType(vo.getBelongType());
        agentRebate.setRemark("");
        agentRebate.setOrderGmtTime(profitCalc.getOrderGmtTime());
        agentRebate.setOrderAuditTime(profitCalc.getOrderAuditTime());
        agentRebate.setOrderAgentId(profitCalc.getAgentId());
        agentRebate.setOrderWithdrawTime(DateUtil.offsetDay(new Date(), 15));
        agentRebate.setKefuId(profitCalc.getKefuId());
        agentRebate.setKefu(profitCalc.getKefu());
        return agentRebate;
    }

    private List<AgentAccount> batchUpdateAgentAccount(List<AgentAccountBill> billList) {
        Map<Long, AgentAccount> agentAccountMap = Maps.newHashMap();
        for (AgentAccountBill bill : billList) {
            Long agentId = bill.getAgentId();
            if (!agentAccountMap.containsKey(agentId)) {
                AgentAccount agentAccount = agentAccountService.findByAgentId(agentId);
                calcRevenue(bill, agentAccount);
                agentAccountMap.put(agentId, agentAccount);
            } else {
                AgentAccount agentAccount = agentAccountMap.get(agentId);
                calcRevenue(bill, agentAccount);
            }
        }
        List<AgentAccount> agentAccounts = Lists.newArrayList();
        for (Map.Entry<Long, AgentAccount> a : agentAccountMap.entrySet()) {
            agentAccounts.add(a.getValue());
        }
        return agentAccounts;

    }

    private void calcRevenue(AgentAccountBill bill, AgentAccount agentAccount) {
        BillTransTypeEnum transType = EnumUtil.getIEnum(BillTransTypeEnum.class, bill.getTransType());
        if (BillTypeEnum.INCOME.getValue().equals(bill.getBillType())) {
            agentAccount.setTotalRevenue(MoneyUtil.add(agentAccount.getTotalRevenue(), bill.getAmount()));
        } else {
            agentAccount.setTotalRevenue(MoneyUtil.sub(agentAccount.getTotalRevenue(), bill.getAmount()));
        }
        switch (transType) {
            case COMMISSION:
                if (BillTypeEnum.INCOME.getValue().equals(bill.getBillType())) {
                    agentAccount.setTotalCommisionRevenue(MoneyUtil.add(agentAccount.getTotalCommisionRevenue(), bill.getAmount()));
                } else {
                    agentAccount.setTotalCommisionRevenue(MoneyUtil.sub(agentAccount.getTotalCommisionRevenue(), bill.getAmount()));
                }

                break;
            case REBATE:
                if (BillTypeEnum.INCOME.getValue().equals(bill.getBillType())) {
                    agentAccount.setTotalRebateRevenue(MoneyUtil.add(agentAccount.getTotalRebateRevenue(), bill.getAmount()));
                    if (OrderTypeEnum.SECKILL.getValue().equals(bill.getOrderType())) {
                        agentAccount.setAvaAmount(MoneyUtil.add(agentAccount.getAvaAmount(), bill.getAmount()));
                    }
                } else {
                    agentAccount.setTotalRebateRevenue(MoneyUtil.sub(agentAccount.getTotalRebateRevenue(), bill.getAmount()));
                    if (OrderTypeEnum.SECKILL.getValue().equals(bill.getOrderType())) {
                        agentAccount.setAvaAmount(MoneyUtil.sub(agentAccount.getAvaAmount(), bill.getAmount()));
                    }
                }

                break;
            case BONUS:
                if (BillTypeEnum.INCOME.getValue().equals(bill.getBillType())) {
                    agentAccount.setTotalBonusRevenue(MoneyUtil.add(agentAccount.getTotalBonusRevenue(), bill.getAmount()));
                    agentAccount.setAvaBonusRevenue(MoneyUtil.add(agentAccount.getAvaBonusRevenue(), bill.getAmount()));
                } else {
                    agentAccount.setTotalBonusRevenue(MoneyUtil.sub(agentAccount.getTotalBonusRevenue(), bill.getAmount()));
                    agentAccount.setAvaBonusRevenue(MoneyUtil.sub(agentAccount.getAvaBonusRevenue(), bill.getAmount()));
                }

                break;
            default:
                break;
        }
    }


    private List<AgentAccountBill> getAgentAccountBillListFromRebateList(List<AgentRebate> rebateList) {
        List<AgentAccountBill> billList = Lists.newArrayList();
        if (CollUtil.isNotEmpty(rebateList)) {
            for (AgentRebate agentRebate : rebateList) {
                billList.add(createBillFromRebate(agentRebate, BillTypeEnum.INCOME, "获得返利"));
            }
        }
        return billList;
    }

    private List<AgentAccountBill> getAgentAccountBillListFromCommList(List<AgentCommission> commissionList) {
        List<AgentAccountBill> billList = Lists.newArrayList();
        if (CollUtil.isNotEmpty(commissionList)) {
            for (AgentCommission agentRebate : commissionList) {
                billList.add(createBillFromCommission(agentRebate, BillTypeEnum.INCOME, "获得佣金"));
            }
        }
        return billList;
    }


    private List<AgentAccountBill> getAgentAccountBillListFromBonusList(List<AgentBonus> bonusList) {
        List<AgentAccountBill> billList = Lists.newArrayList();
        if (CollUtil.isNotEmpty(bonusList)) {
            for (AgentBonus agentRebate : bonusList) {
                billList.add(createBillFromBonus(agentRebate, BillTypeEnum.INCOME, "获得分红"));
            }
        }
        return billList;
    }

    private AgentAccountBill createBillFromRebate(AgentRebate agentRebate, BillTypeEnum billTransType, String remark) {
        AgentAccountBill bill = new AgentAccountBill();
        bill.setAgentId(agentRebate.getAgentId());
        bill.setTransType(BillTransTypeEnum.REBATE.getValue());
        bill.setRemark("订单" + agentRebate.getOrderSn() + remark);
        bill.setAmount(agentRebate.getRebateAmount());
        bill.setBillType(billTransType.getValue());
        bill.setRelationId(agentRebate.getOrderId());
        bill.setOrderType(agentRebate.getRebateType());
        return bill;
    }

    private AgentAccountBill createBillFromCommission(AgentCommission agentCommission, BillTypeEnum billType, String remark) {
        AgentAccountBill bill = new AgentAccountBill();
        bill.setAgentId(agentCommission.getAgentId());
        bill.setTransType(BillTransTypeEnum.COMMISSION.getValue());
        bill.setRemark("订单" + agentCommission.getOrderSn() + remark);
        bill.setAmount(agentCommission.getCommissionAmount());
        bill.setBillType(billType.getValue());
        bill.setRelationId(agentCommission.getOrderId());
        bill.setOrderType(agentCommission.getCommissionFrom());
        return bill;
    }

    private AgentAccountBill createBillFromBonus(AgentBonus agentBonus, BillTypeEnum billType, String remark) {
        AgentAccountBill bill = new AgentAccountBill();
        bill.setAgentId(agentBonus.getAgentId());
        bill.setTransType(BillTransTypeEnum.BONUS.getValue());
        bill.setRemark("订单" + agentBonus.getOrderSn() + remark);
        bill.setAmount(agentBonus.getBonusAmount());
        bill.setBillType(billType.getValue());
        bill.setRelationId(agentBonus.getOrderId());
        bill.setOrderType(agentBonus.getBonusType());
        return bill;
    }

}
